package tr.edu.metu.is550.isell.rest.jersey.provider.injectable;

import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.api.model.Parameter;
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.core.spi.component.ComponentScope;
import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import tr.edu.metu.is550.isell.domain.ErrorCodes;
import tr.edu.metu.is550.isell.rest.message.ErrorMessages;

import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validator;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import java.util.Iterator;
import java.util.Set;

@Provider
@Component
public class ValidMessageInjectableProvider implements InjectableProvider<Valid, Parameter> {

    @Autowired
    Validator validator;


    @Override
    public ComponentScope getScope() {
        return ComponentScope.PerRequest;
    }

    @Override
    public Injectable getInjectable(ComponentContext ic, Valid valid, Parameter parameter) {
        return new ValidEntityInjectable(parameter);
    }

    class ValidEntityInjectable extends AbstractHttpContextInjectable<Object> {


        Parameter parameter = null;

        ValidEntityInjectable(Parameter parameter) {
            this.parameter = parameter;
        }

        @Override
        public Object getValue(HttpContext context) {
            Object entity = context.getRequest().getEntity(parameter.getParameterClass(), parameter.getParameterType(), parameter.getAnnotations());
            validateEntity(entity);
            return entity;
        }

        private void validateEntity(Object entity) {
            if (entity == null)
                return;

            Set<ConstraintViolation<Object>> violations = validator.validate(entity);

            if (violations.isEmpty())
                return;

            ErrorMessages errorMessages = buildErrorMessagesEntity(violations);
            throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST).entity(errorMessages).build());

        }

        private ErrorMessages buildErrorMessagesEntity(Set<ConstraintViolation<Object>> violations) {
            Iterator<ConstraintViolation<Object>> iterator = violations.iterator();
            ErrorMessages errorMessages = new ErrorMessages();
            while (iterator.hasNext()) {
                ConstraintViolation<?> next = iterator.next();
                errorMessages.add(ErrorCodes.INVALID_INPUT, next.getMessage(), next.getPropertyPath().toString());
            }
            return errorMessages;
        }

    }
}
